Add `RTCRtpSource::SenderCaptureTimeOffset` The implementation is according to [1]; an intent was filed on blink-dev (see [2]). The unit tests for `RTCRtpSynchronizationSource.senderCaptureTimeOffset` are an adaption of [3]. Note that `step_timeout` has been replaced with `step_wait` both in the new test and in [3]. This CL needs [4] which fixes the clock offset reference clock in WebRTC. [1] https://w3c.github.io/webrtc-extensions/#dom-rtcrtpcontributingsource-sendercapturetimeoffset [2] https://groups.google.com/a/chromium.org/g/blink-dev/c/SRfE60yI0uc [3] third_party/blink/web_tests/external/wpt/webrtc-extensions/RTCRtpSynchronizationSource-captureTimestamp.html [4] https://webrtc-review.googlesource.com/c/src/+/219163 Bug: chromium:1056230, webrtc:10739 Change-Id: I610e2dac614be27927557939243cb9bbb597780c Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2731987 Reviewed-by: Harald Alvestrand <hta@chromium.org> Commit-Queue: Alessio Bazzica <alessiob@chromium.org> Cr-Commit-Position: refs/heads/master@{#887115} 
diff --git a/webrtc-extensions/RTCRtpSynchronizationSource-captureTimestamp.html b/webrtc-extensions/RTCRtpSynchronizationSource-captureTimestamp.html index 11f2540..60b4ed0 100644 --- a/webrtc-extensions/RTCRtpSynchronizationSource-captureTimestamp.html +++ b/webrtc-extensions/RTCRtpSynchronizationSource-captureTimestamp.html 
@@ -21,12 +21,12 @@  assert_equals(ssrcs.length, 1);  if (ssrcs[0].captureTimestamp != undefined) {  resolve(ssrcs[0].captureTimestamp); - return; + return true;  }  } - t.step_timeout(listen, 0); + return false;  }; - listen(); + t.step_wait(listen, 'No abs-capture-time capture time header extension.');  });  }   @@ -35,7 +35,8 @@  for (const kind of ['audio', 'video']) {  promise_test(async t => {  const [caller, callee] = await initiateSingleTrackCall( - t, {[kind]: true}, false, false); + t, /* caps= */{[kind]: true}, /* absCaptureTimeOffered= */false, + /* absCaptureTimeAnswered= */false);  const receiver = callee.getReceivers()[0];    for (const ssrc of await listenForSSRCs(t, receiver)) { @@ -47,7 +48,8 @@    promise_test(async t => {  const [caller, callee] = await initiateSingleTrackCall( - t, {[kind]: true}, false, false); + t, /* caps= */{[kind]: true}, /* absCaptureTimeOffered= */false, + /* absCaptureTimeAnswered= */false);  const receiver = callee.getReceivers()[0];    for (const ssrc of await listenForSSRCs(t, receiver)) { @@ -59,7 +61,8 @@    promise_test(async t => {  const [caller, callee] = await initiateSingleTrackCall( - t, {[kind]: true}, true, true); + t, /* caps= */{[kind]: true}, /* absCaptureTimeOffered= */true, + /* absCaptureTimeAnswered= */true);  const receiver = callee.getReceivers()[0];  await listenForCaptureTimestamp(t, receiver);  }, '[' + kind + '] getSynchronizationSources() should contain ' + @@ -72,7 +75,8 @@  // `callee`.  promise_test(async t => {  const [caller, callee] = await initiateSingleTrackCall( - t, {audio: true, video: true}, true, true); + t, /* caps= */{audio: true, video: true}, + /* absCaptureTimeOffered= */true, /* absCaptureTimeAnswered= */true);  const receivers = callee.getReceivers();  assert_equals(receivers.length, 2);   
diff --git a/webrtc-extensions/RTCRtpSynchronizationSource-senderCaptureTimeOffset.html b/webrtc-extensions/RTCRtpSynchronizationSource-senderCaptureTimeOffset.html new file mode 100644 index 0000000..63ad9bf --- /dev/null +++ b/webrtc-extensions/RTCRtpSynchronizationSource-senderCaptureTimeOffset.html 
@@ -0,0 +1,92 @@ +<!doctype html> +<meta charset=utf-8> +<!-- This file contains a test that waits for 2 seconds. --> +<meta name="timeout" content="long"> +<title>senderCaptureTimeOffset attribute in RTCRtpSynchronizationSource</title> +<div><video id="remote" width="124" height="124" autoplay></video></div> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/webrtc/RTCPeerConnection-helper.js"></script> +<script src="/webrtc/RTCStats-helper.js"></script> +<script src="/webrtc-extensions/RTCRtpSynchronizationSource-helper.js"></script> +<script> +'use strict'; + +function listenForSenderCaptureTimeOffset(t, receiver) { + return new Promise((resolve) => { + function listen() { + const ssrcs = receiver.getSynchronizationSources(); + assert_true(ssrcs != undefined); + if (ssrcs.length > 0) { + assert_equals(ssrcs.length, 1); + if (ssrcs[0].captureTimestamp != undefined) { + resolve(ssrcs[0].senderCaptureTimeOffset); + return true; + } + } + return false; + }; + t.step_wait(listen, 'No abs-capture-time capture time header extension.'); + }); +} + +// Passes if `getSynchronizationSources()` contains `senderCaptureTimeOffset` if +// and only if expected. +for (const kind of ['audio', 'video']) { + promise_test(async t => { + const [caller, callee] = await initiateSingleTrackCall( + t, /* caps= */{[kind]: true}, /* absCaptureTimeOffered= */false, + /* absCaptureTimeAnswered= */false); + const receiver = callee.getReceivers()[0]; + + for (const ssrc of await listenForSSRCs(t, receiver)) { + assert_equals(typeof ssrc.senderCaptureTimeOffset, 'undefined'); + } + }, '[' + kind + '] getSynchronizationSources() should not contain ' + + 'senderCaptureTimeOffset if absolute capture time RTP header extension ' + + 'is not offered'); + + promise_test(async t => { + const [caller, callee] = await initiateSingleTrackCall( + t, /* caps= */{[kind]: true}, /* absCaptureTimeOffered= */false, + /* absCaptureTimeAnswered= */false); + const receiver = callee.getReceivers()[0]; + + for (const ssrc of await listenForSSRCs(t, receiver)) { + assert_equals(typeof ssrc.senderCaptureTimeOffset, 'undefined'); + } + }, '[' + kind + '] getSynchronizationSources() should not contain ' + + 'senderCaptureTimeOffset if absolute capture time RTP header extension ' + + 'is offered, but not answered'); + + promise_test(async t => { + const [caller, callee] = await initiateSingleTrackCall( + t, /* caps= */{[kind]: true}, /* absCaptureTimeOffered= */true, + /* absCaptureTimeAnswered= */true); + const receiver = callee.getReceivers()[0]; + let senderCaptureTimeOffset = await listenForSenderCaptureTimeOffset( + t, receiver); + assert_true(senderCaptureTimeOffset != undefined); + }, '[' + kind + '] getSynchronizationSources() should contain ' + + 'senderCaptureTimeOffset if absolute capture time RTP header extension ' + + 'is negotiated'); +} + +// Passes if `senderCaptureTimeOffset` is zero, which is expected since the test +// creates a local peer connection between `caller` and `callee`. +promise_test(async t => { + const [caller, callee] = await initiateSingleTrackCall( + t, /* caps= */{audio: true, video: true}, + /* absCaptureTimeOffered= */true, /* absCaptureTimeAnswered= */true); + const receivers = callee.getReceivers(); + assert_equals(receivers.length, 2); + + for (let i = 0; i < 2; ++i) { + let senderCaptureTimeOffset = await listenForSenderCaptureTimeOffset( + t, receivers[i]); + assert_equals(senderCaptureTimeOffset, 0); + } +}, 'Audio and video RTCRtpSynchronizationSource.senderCaptureTimeOffset must ' + + 'be zero'); + +</script>